﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Data.Objects;
using System.Data;
using Automaton.EntityTracking;

namespace Automaton.DataModel
{
    public class Repository : BaseRepository, IRepository
    {

        #region Base Implementation

        private bool _disposed;

        public Repository(AutomatonEntities context)
        {
            this._context = context;
            this._contextReused = true;
        }

        #endregion

        #region IRepository Members

        public int Add<T>(T entity)
        {
            AutomatonEntities context = GetObjectContext();
            context.AddObject(typeof(T).Name, entity);
            int result = context.SaveChanges();
            ReleaseObjectContextIfNotReused();
            return result;
        }

        public long Count<T>()
        {
            AutomatonEntities context = GetObjectContext();
            var query = new ObjectQuery<T>(
                typeof(T).Name,
                context,
                MergeOption.NoTracking);
            int count = query.Count();
            ReleaseObjectContextIfNotReused();
            return count;
        }

        public long Count<T>(Expression<Func<T, bool>> expression)
        {
            AutomatonEntities context = GetObjectContext();
            var query = new ObjectQuery<T>(
                typeof(T).Name,
                context,
                MergeOption.NoTracking)
                .Where(expression);
            int count = query.Count();
            ReleaseObjectContextIfNotReused();
            return count;
        }

        public int Delete<T>(T entity)
        {
            AutomatonEntities context = GetObjectContext();
            object originalItem;
            EntityKey key = context.CreateEntityKey(typeof(T).Name, entity);
            if (context.TryGetObjectByKey(key, out originalItem))
            {
                context.DeleteObject(originalItem);
            }
            int result = context.SaveChanges();
            ReleaseObjectContextIfNotReused();
            return result;
        }

        public IList<T> GetAll<T>()
        {
            AutomatonEntities context = GetObjectContext();
            IList<T> list = context
                .CreateQuery<T>(
                "[" + typeof(T).Name + "]")
                .ToList();
            ReleaseObjectContextIfNotReused();
            return list;
        }

        public IList<T> GetAll<T>(Expression<Func<T, bool>> expression)
        {
            AutomatonEntities context = GetObjectContext();
            IList<T> list = context
                .CreateQuery<T>(
                "[" + typeof(T).Name + "]")
                .Where(expression)
                .ToList();
            ReleaseObjectContextIfNotReused();
            return list;
        }

        public T GetSingle<T>(Expression<Func<T, bool>> expression)
        {
            AutomatonEntities context = GetObjectContext();
            T result = context
                .CreateQuery<T>(
                "[" + typeof(T).Name + "]")
                .Where(expression)
                .FirstOrDefault();
            ReleaseObjectContextIfNotReused();
            return result;
        }

        public int Update<T>(T entity)
        {
            AutomatonEntities context = GetObjectContext();
            object originalItem;
            EntityKey key = context.CreateEntityKey(typeof(T).Name, entity);
            if (context.TryGetObjectByKey(key, out originalItem))
            {
                context.ApplyPropertyChanges(typeof(T).Name, entity);
            }
            int result = context.SaveChanges();
            ReleaseObjectContextIfNotReused();
            return result;
        }

        #endregion

        #region Disposable

        public void Dispose()
        {
            DisposeObject(true);
            GC.SuppressFinalize(this);
        }

        ~Repository()
        {
            DisposeObject(false);
        }

        private void DisposeObject(bool disposing)
        {
            if (_disposed)
            {
                return;
            }
            if (disposing)
            {
                if (_context != null)
                {
                    _context.Dispose();
                }
                _disposed = true;
            }
        }

        #endregion
    }
}
